package znet

import (
	"fmt"
	"my-zinx/utils"
	"my-zinx/ziface"
)

/**
虽然go的调度算法已经很极致了，但是大数量的goroutine仍然有一些不必要的切换成本
使用消息队列来缓冲工作协程数据
*/

// MsgHandle 消息管理模块的接口
type MsgHandle struct {
	APIs           map[uint64]ziface.IRouter // key: msgID; value: router 不同的消息ID对应不同的处理方法
	WorkerPoolSize uint64                    // 业务协程池数量(队列数量)
	TaskQueue      []chan ziface.IRequest    // 业务工作Worker池(请求队列)
}

func (m *MsgHandle) DoMsgHandler(request ziface.IRequest) {
	handler, ok := m.APIs[request.GetMsgID()]
	if !ok {
		fmt.Printf("handler [MsgID:%d] not found\n", request.GetMsgID())
		return
	}

	handler.PreHandle(request)
	handler.Handle(request)
	handler.PostHandle(request)
}

func (m *MsgHandle) AddRouter(msgID uint64, router ziface.IRouter) {
	if _, ok := m.APIs[msgID]; ok {
		fmt.Printf("handler [MsgID:%d] is exists\n", msgID)
		return
	}
	m.APIs[msgID] = router
}

func (m *MsgHandle) StartWorkerPool() {
	for i := 0; i < int(m.WorkerPoolSize); i++ {
		m.TaskQueue[i] = make(chan ziface.IRequest, utils.Env.MaxWorkerTaskLen)
		go m.StartOneWorker(i, m.TaskQueue[i])
	}
}

func (m *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Printf("workerID:%d is working\n", workerID)

	for {
		select {
		case request := <-taskQueue: // 取任务，做任务
			m.DoMsgHandler(request)
		}
	}
}

func (m *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) {
	// workerID 应按某种算法算出，分配给当前协程执行
	// 这里采用轮询平均分配
	workerID := request.GetConnection().GetConnID() % m.WorkerPoolSize

	fmt.Printf("connID: %d [MsgID:%d] distrute to workerID: %d do task\n",
		request.GetConnection().GetConnID(), request.GetMsgID(), workerID)

	m.TaskQueue[workerID] <- request
}

func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		APIs:           make(map[uint64]ziface.IRouter),
		WorkerPoolSize: utils.Env.WorkerPoolSize,
		TaskQueue:      make([]chan ziface.IRequest, utils.Env.MaxWorkerTaskLen),
	}
}
